Plotting points on a map
The Starbucks data, compiled by Danny Kaplan and provided by Alicia Johnson, contains information about every Starbucks in the world at the time the data were collected. It includes the Latitude and Longitude of each location. Let’s start by using familiar plotting tools
ggplot(data=Starbucks) +
geom_point(aes(x = Longitude, y = Latitude),
alpha = 0.2,
size = .1)

The point pattern probably looks familiar. To highlight the geographical nature of this scatterplot, we can superimpose the points on top of a map, using the ggmap() function from the ggmap library.
NOTE: we used to be able to easily bring in Google maps. As of mid-2018, in order to bring those in, you need to have a registered API key. If you want to do that, see google_key in the help. Then, see the documentation for get_map(). We will bring in other types of maps since Google maps are harder to do now and require you to submit credit card information.
Instead, we bring in a stamen map (there are others you could try, but we’ll stick with this). You can also take a look at stamen maps on their website. First, let’s look at an example.
# Get the map information
world <- get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
# Plot the points on the map
ggmap(world) + # creates the map "background"
geom_point(data = Starbucks,
aes(x = Longitude, y = Latitude),
alpha = .3,
size = .1) +
theme_map()

Next, we will walk through the get_stamenmap() function arguments. The code below is what was used to get the world map information.
get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
bbox
get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
The bbox argument tells it the minimum and maximum latitude and longitude points. So, left is the minimum longitude, right is the maximum longitude, bottom is the minimum latitude, and top is the maximum latitude. I found it helpful to go to openstreetmap: zoom in on the area of interest, click export, and you will see all the values you need. I had to modify them slightly, which you can do after your initial plot.
maptype
get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
The maptype tells it the style of the map. Check out the different options by looking in the get_stamenmap help (type ?get_stamenmap in the console).
zoom
get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
When you make a large area, you need to decrease the zoom, otherwise it will take too long to load. So, it’s a good idea to start with a small zoom and you can always make it bigger if you want. This might seem counter-intuitive at first. I think of the zoom level as the level of detail. So, smaller numbers show less detail and larger numbers more detail. I often go to the stamanmaps webpage and search for the location I’m mapping. Then, in the URL, you can see the zoom number. For example, this link is a map of St. Paul: http://maps.stamen.com/#terrain/12/44.9531/-93.0904. Notice the number 12 next to /#terrain/. That means it is zoomed in at 12.
ggmap()
We save the the map information from get_stamenmap() to a named value and then use it in ggmap():
# Get the map information
world <- get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
# Plot the points on the map
ggmap(world) + # creates the map "background"
geom_point(data = Starbucks,
aes(x = Longitude, y = Latitude),
alpha = .3,
size = .1) +
theme_map()
The ggmap() function will print the “background” map. Think of it as the providing the canvas on which we will plot. This takes the place of our usual ggplot().
ggmap(world)

After that, we can use the geom_XXX() functions from ggplot2 that we are used to in order to put points, lines, etc. on top of the map. But, we need to remember to also provide the data we are using in the geom_XXX() function(s) we use since we do not have the ggplot() function in which to provide it.
# Get the map information
world <- get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
# Plot the points on the map
ggmap(world) + # creates the map "background"
geom_point(data = Starbucks,
aes(x = Longitude, y = Latitude),
alpha = .3,
size = .1) +
theme_map()
theme_map()
The last thing I did in the code was to add theme_map(). This is optional, but I often find it makes it look nice.
# Get the map information
world <- get_stamenmap(
bbox = c(left = -180, bottom = -57, right = 179, top = 82.1),
maptype = "terrain",
zoom = 2)
# Plot the points on the map
ggmap(world) + # creates the map "background"
geom_point(data = Starbucks,
aes(x = Longitude, y = Latitude),
alpha = .3,
size = .1) +
theme_map()

So, the final map as a world map as the background with points plotted on top that show the Starbucks locations. The points are .1 of their usual size and have a transparency level of .3.
Demo video
Resources
Your turn!
LS0tCnRpdGxlOiAiTWFwcGluZyBkYXRhIGluIFIiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgojIyBTZXR1cAoKV2VsY29tZSB0byBhbm90aGVyIHR1dG9yaWFsIGZvciB0aGlzIGNsYXNzLCBDT01QL1NUQVQgMTEyOiAqSW50cm9kdWN0aW9uIHRvIERhdGEgU2NpZW5jZSohIEl0IHdpbGwgYmUgc2ltaWxhciB0byB0aGUgb3RoZXJzLCBpbmNsdWRpbmcgZGVtbyB2aWRlb3MgYW5kIGZpbGVzIGVtYmVkZGVkIGluIHRoaXMgZG9jdW1lbnQgYW5kIHByYWN0aWNlIHByb2JsZW1zIHdpdGggaGludHMgb3Igc29sdXRpb25zIGF0IHRoZSBlbmQuIFRoZXJlIGFyZSBzb21lIG5ldyBsaWJyYXJpZXMsIHNvIGJlIHN1cmUgdG8gaW5zdGFsbCB0aG9zZSBmaXJzdC4KCkFzIG1vc3Qgb2Ygb3VyIGZpbGVzIGRvLCB3ZSBzdGFydCB0aGlzIG9uZSB3aXRoIHRocmVlIFIgY29kZSBjaHVua3M6IDEuIG9wdGlvbnMsIDIuIGxpYnJhcmllcyBhbmQgc2V0dGluZ3MsIDMuIGRhdGEuIAoKYGBge3Igc2V0dXB9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UpCmBgYAoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgICMgZm9yIGRhdGEgY2xlYW5pbmcgYW5kIHBsb3R0aW5nCmxpYnJhcnkoZ29vZ2xlc2hlZXRzNCkgIyBmb3IgcmVhZGluZyBnb29nbGVzaGVldCBkYXRhCmxpYnJhcnkobHVicmlkYXRlKSAgICAgIyBmb3IgZGF0ZSBtYW5pcHVsYXRpb24KbGlicmFyeShwYWxtZXJwZW5ndWlucykjIGZvciBQYWxtZXIgcGVuZ3VpbiBkYXRhCmxpYnJhcnkobWFwcykgICAgICAgICAgIyBmb3IgbWFwIGRhdGEKbGlicmFyeShnZ21hcCkgICAgICAgICAjIGZvciBtYXBwaW5nIHBvaW50cyBvbiBtYXBzCmxpYnJhcnkoZ2d0aGVtZXMpICAgICAgIyBmb3IgbW9yZSB0aGVtZXMgKGluY2x1ZGluZyB0aGVtZV9tYXAoKSkKZ3M0X2RlYXV0aCgpICAgICAgICAgICAjIFRvIG5vdCBoYXZlIHRvIGF1dGhvcml6ZSBlYWNoIHRpbWUgeW91IGtuaXQuCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpCmBgYAoKYGBge3IgbXlfbGlicmFyaWVzLCBpbmNsdWRlPUZBTFNFfQojIExpc2EgbmVlZHMgdGhpcywgc3R1ZGVudHMgZG9uJ3QKbGlicmFyeShkb3dubG9hZHRoaXMpICMgZm9yIGluY2x1ZGluZyBkb3dubG9hZCBidXR0b25zIGZvciBmaWxlcwpsaWJyYXJ5KGZsYWlyKSAjIGZvciBoaWdobGlnaHRpbmcgY29kZQpgYGAKCmBgYHtyIGRhdGF9CiMgU3RhcmJ1Y2tzIGxvY2F0aW9ucwpTdGFyYnVja3MgPC0gcmVhZF9jc3YoImh0dHBzOi8vd3d3Lm1hY2FsZXN0ZXIuZWR1L35ham9obnMyNC9EYXRhL1N0YXJidWNrcy5jc3YiKQpgYGAKCiMjIExlYXJuaW5nIEdvYWxzCgpBZnRlciB0aGlzIHR1dG9yaWFsLCB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gZG8gdGhlIGZvbGxvd2luZzoKCgoKIyMgUGxvdHRpbmcgcG9pbnRzIG9uIGEgbWFwCgpUaGUgYFN0YXJidWNrc2AgZGF0YSwgY29tcGlsZWQgYnkgRGFubnkgS2FwbGFuIGFuZCBwcm92aWRlZCBieSBBbGljaWEgSm9obnNvbiwgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgZXZlcnkgU3RhcmJ1Y2tzIGluIHRoZSB3b3JsZCBhdCB0aGUgdGltZSB0aGUgZGF0YSB3ZXJlIGNvbGxlY3RlZC4gSXQgaW5jbHVkZXMgdGhlIGBMYXRpdHVkZWAgYW5kIGBMb25naXR1ZGVgIG9mIGVhY2ggbG9jYXRpb24uICBMZXQncyBzdGFydCBieSB1c2luZyBmYW1pbGlhciBwbG90dGluZyB0b29scwoKYGBge3J9CmdncGxvdChkYXRhPVN0YXJidWNrcykgKwogIGdlb21fcG9pbnQoYWVzKHggPSBMb25naXR1ZGUsIHkgPSBMYXRpdHVkZSksIAogICAgICAgICAgICAgYWxwaGEgPSAwLjIsIAogICAgICAgICAgICAgc2l6ZSA9IC4xKQpgYGAKClRoZSBwb2ludCBwYXR0ZXJuIHByb2JhYmx5IGxvb2tzIGZhbWlsaWFyLiAgVG8gaGlnaGxpZ2h0IHRoZSBnZW9ncmFwaGljYWwgbmF0dXJlIG9mIHRoaXMgc2NhdHRlcnBsb3QsIHdlIGNhbiBzdXBlcmltcG9zZSB0aGUgcG9pbnRzIG9uIHRvcCBvZiBhIG1hcCwgdXNpbmcgdGhlIGBnZ21hcCgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgZ2dtYXBgIGxpYnJhcnkuIAoKKipOT1RFKio6IHdlIHVzZWQgdG8gYmUgYWJsZSB0byAqZWFzaWx5KiBicmluZyBpbiBHb29nbGUgbWFwcy4gQXMgb2YgbWlkLTIwMTgsIGluIG9yZGVyIHRvIGJyaW5nIHRob3NlIGluLCB5b3UgbmVlZCB0byBoYXZlIGEgcmVnaXN0ZXJlZCBBUEkga2V5LiBJZiB5b3Ugd2FudCB0byBkbyB0aGF0LCBzZWUgYGdvb2dsZV9rZXlgIGluIHRoZSBoZWxwLiBUaGVuLCBzZWUgdGhlIGRvY3VtZW50YXRpb24gZm9yIGBnZXRfbWFwKClgLiBXZSB3aWxsIGJyaW5nIGluIG90aGVyIHR5cGVzIG9mIG1hcHMgc2luY2UgR29vZ2xlIG1hcHMgYXJlIGhhcmRlciB0byBkbyBub3cgYW5kIHJlcXVpcmUgeW91IHRvIHN1Ym1pdCBjcmVkaXQgY2FyZCBpbmZvcm1hdGlvbi4KCkluc3RlYWQsIHdlIGJyaW5nIGluIGEgc3RhbWVuIG1hcCAodGhlcmUgYXJlIG90aGVycyB5b3UgY291bGQgdHJ5LCBidXQgd2UnbGwgc3RpY2sgd2l0aCB0aGlzKS4gWW91IGNhbiBhbHNvIHRha2UgYSBsb29rIGF0IHN0YW1lbiBtYXBzIG9uIHRoZWlyIFt3ZWJzaXRlXShodHRwOi8vbWFwcy5zdGFtZW4uY29tLyN3YXRlcmNvbG9yLzEyLzM3Ljc3MDYvLTEyMi4zNzgyKS4gRmlyc3QsIGxldCdzIGxvb2sgYXQgYW4gZXhhbXBsZS4gCgpgYGB7ciBzdGFyYnVja3MtbWFwfQojIEdldCB0aGUgbWFwIGluZm9ybWF0aW9uCndvcmxkIDwtIGdldF9zdGFtZW5tYXAoCiAgICBiYm94ID0gYyhsZWZ0ID0gLTE4MCwgYm90dG9tID0gLTU3LCByaWdodCA9IDE3OSwgdG9wID0gODIuMSksIAogICAgbWFwdHlwZSA9ICJ0ZXJyYWluIiwKICAgIHpvb20gPSAyKQoKIyBQbG90IHRoZSBwb2ludHMgb24gdGhlIG1hcApnZ21hcCh3b3JsZCkgKyAjIGNyZWF0ZXMgdGhlIG1hcCAiYmFja2dyb3VuZCIKICBnZW9tX3BvaW50KGRhdGEgPSBTdGFyYnVja3MsIAogICAgICAgICAgICAgYWVzKHggPSBMb25naXR1ZGUsIHkgPSBMYXRpdHVkZSksIAogICAgICAgICAgICAgYWxwaGEgPSAuMywgCiAgICAgICAgICAgICBzaXplID0gLjEpICsKICB0aGVtZV9tYXAoKQpgYGAKCk5leHQsIHdlIHdpbGwgd2FsayB0aHJvdWdoIHRoZSBgZ2V0X3N0YW1lbm1hcCgpYCBmdW5jdGlvbiBhcmd1bWVudHMuIFRoZSBjb2RlIGJlbG93IGlzIHdoYXQgd2FzIHVzZWQgdG8gZ2V0IHRoZSB3b3JsZCBtYXAgaW5mb3JtYXRpb24uCgpgYGB7ciBnZXRfc3RhbWVubWFwLWNvZGUsIGV2YWw9RkFMU0V9CmdldF9zdGFtZW5tYXAoCiAgICBiYm94ID0gYyhsZWZ0ID0gLTE4MCwgYm90dG9tID0gLTU3LCByaWdodCA9IDE3OSwgdG9wID0gODIuMSksIAogICAgbWFwdHlwZSA9ICJ0ZXJyYWluIiwKICAgIHpvb20gPSAyKQpgYGAKCioqYGJib3hgKioKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygiZ2V0X3N0YW1lbm1hcC1jb2RlIiwgZXZhbCA9IEZBTFNFKSAlPiUgCiAgZmxhaXIoImJib3ggPSAiKQpgYGAKCgpUaGUgYGJib3hgIGFyZ3VtZW50IHRlbGxzIGl0IHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgcG9pbnRzLiBTbywgbGVmdCBpcyB0aGUgbWluaW11bSBsb25naXR1ZGUsIHJpZ2h0IGlzIHRoZSBtYXhpbXVtIGxvbmdpdHVkZSwgYm90dG9tIGlzIHRoZSBtaW5pbXVtIGxhdGl0dWRlLCBhbmQgdG9wIGlzIHRoZSBtYXhpbXVtIGxhdGl0dWRlLiBJIGZvdW5kIGl0IGhlbHBmdWwgdG8gZ28gdG8gW29wZW5zdHJlZXRtYXBdKGh0dHBzOi8vd3d3Lm9wZW5zdHJlZXRtYXAub3JnKTogem9vbSBpbiBvbiB0aGUgYXJlYSBvZiBpbnRlcmVzdCwgY2xpY2sgZXhwb3J0LCBhbmQgeW91IHdpbGwgc2VlIGFsbCB0aGUgdmFsdWVzIHlvdSBuZWVkLiBJIGhhZCB0byBtb2RpZnkgdGhlbSBzbGlnaHRseSwgd2hpY2ggeW91IGNhbiBkbyBhZnRlciB5b3VyIGluaXRpYWwgcGxvdC4KCioqYG1hcHR5cGVgKioKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygiZ2V0X3N0YW1lbm1hcC1jb2RlIiwgZXZhbCA9IEZBTFNFKSAlPiUgCiAgZmxhaXIoIm1hcHR5cGUgPSAiKQpgYGAKClRoZSBgbWFwdHlwZWAgdGVsbHMgaXQgdGhlIHN0eWxlIG9mIHRoZSBtYXAuIENoZWNrIG91dCB0aGUgZGlmZmVyZW50IG9wdGlvbnMgYnkgbG9va2luZyBpbiB0aGUgYGdldF9zdGFtZW5tYXBgIGhlbHAgKHR5cGUgYD9nZXRfc3RhbWVubWFwYCBpbiB0aGUgY29uc29sZSkuCgoqKmB6b29tYCoqCgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoImdldF9zdGFtZW5tYXAtY29kZSIsIGV2YWwgPSBGQUxTRSkgJT4lIAogIGZsYWlyKCJ6b29tID0gIikKYGBgCgpXaGVuIHlvdSBtYWtlIGEgbGFyZ2UgYXJlYSwgeW91IG5lZWQgdG8gZGVjcmVhc2UgdGhlIHpvb20sIG90aGVyd2lzZSBpdCB3aWxsIHRha2UgdG9vIGxvbmcgdG8gbG9hZC4gU28sIGl0J3MgYSBnb29kIGlkZWEgdG8gc3RhcnQgd2l0aCBhIHNtYWxsIHpvb20gYW5kIHlvdSBjYW4gYWx3YXlzIG1ha2UgaXQgYmlnZ2VyIGlmIHlvdSB3YW50LiBUaGlzIG1pZ2h0IHNlZW0gY291bnRlci1pbnR1aXRpdmUgYXQgZmlyc3QuIEkgdGhpbmsgb2YgdGhlIHpvb20gbGV2ZWwgYXMgdGhlIGxldmVsIG9mIGRldGFpbC4gU28sIHNtYWxsZXIgbnVtYmVycyBzaG93IGxlc3MgZGV0YWlsIGFuZCBsYXJnZXIgbnVtYmVycyBtb3JlIGRldGFpbC4gSSBvZnRlbiBnbyB0byB0aGUgc3RhbWFubWFwcyB3ZWJwYWdlIGFuZCBzZWFyY2ggZm9yIHRoZSBsb2NhdGlvbiBJJ20gbWFwcGluZy4gVGhlbiwgaW4gdGhlIFVSTCwgeW91IGNhbiBzZWUgdGhlIHpvb20gbnVtYmVyLiBGb3IgZXhhbXBsZSwgdGhpcyBsaW5rICBpcyBhIG1hcCBvZiBTdC4gUGF1bDogW2h0dHA6Ly9tYXBzLnN0YW1lbi5jb20vI3RlcnJhaW4vMTIvNDQuOTUzMS8tOTMuMDkwNF0oaHR0cDovL21hcHMuc3RhbWVuLmNvbS8jdGVycmFpbi8xMi80NC45NTMxLy05My4wOTA0KS4gTm90aWNlIHRoZSBudW1iZXIgYDEyYCBuZXh0IHRvICBgLyN0ZXJyYWluL2AuIFRoYXQgbWVhbnMgaXQgaXMgem9vbWVkIGluIGF0IDEyLiAKCioqYGdnbWFwKClgKioKCldlIHNhdmUgdGhlIHRoZSBtYXAgaW5mb3JtYXRpb24gZnJvbSBgZ2V0X3N0YW1lbm1hcCgpYCB0byBhIG5hbWVkIHZhbHVlIGFuZCB0aGVuIHVzZSBpdCBpbiBgZ2dtYXAoKWA6CgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoInN0YXJidWNrcy1tYXAiLCBldmFsID0gRkFMU0UpICU+JSAKICBmbGFpcigid29ybGQgPC0iKSAlPiUgCiAgZmxhaXIoIndvcmxkIikKYGBgCgpUaGUgYGdnbWFwKClgIGZ1bmN0aW9uIHdpbGwgcHJpbnQgdGhlICJiYWNrZ3JvdW5kIiBtYXAuIFRoaW5rIG9mIGl0IGFzIHRoZSBwcm92aWRpbmcgdGhlIGNhbnZhcyBvbiB3aGljaCB3ZSB3aWxsIHBsb3QuIFRoaXMgdGFrZXMgdGhlIHBsYWNlIG9mIG91ciB1c3VhbCBgZ2dwbG90KClgLgoKYGBge3J9CmdnbWFwKHdvcmxkKQpgYGAKCkFmdGVyIHRoYXQsIHdlIGNhbiB1c2UgdGhlIGBnZW9tX1hYWCgpYCBmdW5jdGlvbnMgZnJvbSBgZ2dwbG90MmAgdGhhdCB3ZSBhcmUgdXNlZCB0byBpbiBvcmRlciB0byBwdXQgcG9pbnRzLCBsaW5lcywgZXRjLiBvbiB0b3Agb2YgdGhlIG1hcC4gQnV0LCB3ZSBuZWVkIHRvIHJlbWVtYmVyIHRvIGFsc28gcHJvdmlkZSB0aGUgZGF0YSB3ZSBhcmUgdXNpbmcgaW4gdGhlIGBnZW9tX1hYWCgpYCBmdW5jdGlvbihzKSB3ZSB1c2Ugc2luY2Ugd2UgZG8gbm90IGhhdmUgdGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24gaW4gd2hpY2ggdG8gcHJvdmlkZSBpdC4gCgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoInN0YXJidWNrcy1tYXAiLCBldmFsID0gRkFMU0UpICU+JSAKICBmbGFpcigiZGF0YSA9IFN0YXJidWNrcyIpCmBgYAoKCioqYHRoZW1lX21hcCgpYCoqCgpUaGUgbGFzdCB0aGluZyBJIGRpZCBpbiB0aGUgY29kZSB3YXMgdG8gYWRkIGB0aGVtZV9tYXAoKWAuIFRoaXMgaXMgb3B0aW9uYWwsIGJ1dCBJIG9mdGVuIGZpbmQgaXQgbWFrZXMgaXQgbG9vayBuaWNlLgoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJzdGFyYnVja3MtbWFwIikgJT4lIAogIGZsYWlyKCJ0aGVtZV9tYXAoKSIpCmBgYAoKU28sIHRoZSBmaW5hbCBtYXAgYXMgYSB3b3JsZCBtYXAgYXMgdGhlIGJhY2tncm91bmQgd2l0aCBwb2ludHMgcGxvdHRlZCBvbiB0b3AgdGhhdCBzaG93IHRoZSBTdGFyYnVja3MgbG9jYXRpb25zLiBUaGUgcG9pbnRzIGFyZSAuMSBvZiB0aGVpciB1c3VhbCBzaXplIGFuZCBoYXZlIGEgdHJhbnNwYXJlbmN5IGxldmVsIG9mIC4zLiAKCgoKIyMjIERlbW8gdmlkZW8KCiMjIyBSZXNvdXJjZXMKCiMjIyBZb3VyIHR1cm4hCgoKCiMjIENob3JvcGxldGhzCgoKIyMjIFlvdXIgdHVybiEKCgojIyBVc2luZyBgbGVhZmxldGAgdG8gY3JlYXRlIG1hcHMK